'use strict';
const Link = require('./lib/link/link.js');
const route = require('./lib/link/route.js');
const Format = require('./lib/frame/format.js');
const listener = require('./lib/listener.js');
const retransmit = require('./lib/retransmit.js');
const checking = require('./lib/checking.js');
const centerId = Buffer.alloc(8, 0x00);
const broadId = Buffer.alloc(8, 0xff);

function memcmp(dst, src, len) {
  for (let i = 0; i < len; i++) {
    if (dst[i] != src[i]) {
      return -1;
    }
  }
  return 0;
}

class Fiip {
  constructor() {
    this.id = null;
    this.rxd = new Format();
    this.remanentBufs = {}; // 残余数据
    this.protocols = {}; // 子协议
  }

  init(relay = 1) {
    this.relay = relay;
    if (!this.getProtocol(0)) {
      this.addProtocol(0, { solve: listener.solve });
    }
    setInterval(retransmit.transmit, 1000);
  }
  setId(id) {
    this.id = id;
  }
  addProtocol(type, protocol) {
    if (this.protocols[type]) {
      console.log('fiip protocol %s has been replaced.', type.toString(16));
    }
    this.protocols[type] = { ...protocol, type };
  }
  getProtocol(type) {
    return this.protocols[type];
  }
  sendOriginal(data, dataLen, link) {
    console.log('send to link:%s; %s:%d:', link.type.toString(16), link.address, link.port);
    console.log(data.slice(0, dataLen).toString('hex'));

    data = Buffer.from(data);
    link.send(data, dataLen, link);
  }
  broadcast(format) {
    format.setVar('ttl', Buffer.from([0x08]), 1);
    format.setVar('control', Buffer.from([0x00]), 1);
    format.setVar('srcAddr', this.id, 8);
    format.getData();
    for (const i in route.routes) {
      this.sendOriginal(format.buf, format.bufLen, route.routes[i]);
    }
  }
  request(format, id, link) {
    if (id == null) {
      id = format.msg.dstAddr;
    } else {
      format.setVar('dstAddr', id, 8);
    }
    if (link == null) {
      link = route.find(id);
    }

    if (link) {
      format.setVar('ttl', Buffer.from([0x08]), 1);
      format.setVar('control', Buffer.from([0x00]), 1);
      format.setVar('srcAddr', this.id, 8);
      format.getData();
      retransmit.add(format.buf, format.bufLen, link);
      this.sendOriginal(format.buf, format.bufLen, link);
    } else {
      console.log('fiip no route,can not request.');
    }
  }
  respond(format, msg, error, body) {
    const link = route.find(msg.srcAddr);
    if (!link) {
      console.log('fiip no route,can not respond.');
      return;
    }

    let msg_cmd = Buffer.from(msg.cmd);
    msg_cmd[0] = msg_cmd[0] | 0x80;
    format.setVar('ttl', Buffer.from([0x08]), 1);
    format.setVar('control', Buffer.from([0x01]), 1);
    format.setVar('srcAddr', this.id, 8);
    format.setVar('dstAddr', msg.srcAddr, 8);
    format.setVar('cmd', msg_cmd, 2);
    format.setVar('body', body, body.length);
    format.setVar('index', msg.index, 2);
    format.setVar('errno', Buffer.from([error]), 1);
    format.getData();
    this.sendOriginal(format.buf, format.bufLen, link);
  }
  recv(buf, bufLen, link) {
    let recvStatus = -1;
    if (this.remanentBufs[link.type + link.address + link.port]) {
      const remanentBuf = this.remanentBufs[link.type + link.address + link.port];
      recvStatus = remanentBuf.rxd.recv(buf, bufLen);
      if (recvStatus == 0) {
        this.rxd = remanentBuf.rxd;
        delete this.remanentBufs[link.type + link.address + link.port];
      } else {
        return;
      }
    }
    if (recvStatus == -1) {
      recvStatus = this.rxd.recv(buf, bufLen);
    }
    if (recvStatus == 0) {
      console.log('recv from link:%s; %s:%d:', link.type.toString(16), link.address, link.port);
      console.log(this.rxd.buf.slice(0, this.rxd.bufLen).toString('hex'));
      const msg = this.rxd.getMsg();
      if (msg) {
        route.update(msg.srcAddr, link); // 更新路由

        if (memcmp(msg.dstAddr, this.id, 8) == 0) {
          // 执行
          if (msg.control[0] == 0x01) {
            retransmit.remove((msg.index[0] << 8) + msg.index[1]);
          }

          if (this.protocols[msg.type[0]]) {
            this.protocols[msg.type[0]].solve(msg, link);
          } else {
            console.log('fiip no protocol type:%s.', msg.type[0].toString(16));
          }
        } else if (this.relay) {
          // 转发
          const link = route.find(msg.dstAddr);
          if (link) {
            if (msg.ttl[0] > 0) {
              this.bufLen = this.bufLen - 7;
              this.rxd.setVar('ttl', Buffer.from([msg.ttl[0] - 1]), 1);
              this.rxd.getData();
              this.sendOriginal(this.rxd.buf, this.rxd.bufLen, link);
            } else {
              console.log('fiip TTL is %d.', msg.ttl[0]);
            }
          } else {
            console.log('fiip no route,can not forward.');
          }
        }
      } else {
        console.log('fiip crc error.');
      }
    } else {
      if (recvStatus == 1) {
        this.remanentBufs[link.type + link.address + link.port] = {
          link,
          rxd: this.rxd,
        };
        this.rxd = new Format();
      } else {
        console.log('bufRecver status: %d.', recvStatus);
      }
    }
  }
  connect(id, link) {
    route.remove(id);
    route.update(id, link);
    route.fix(id);
  }
}
Fiip.prototype.centerId = centerId;
Fiip.prototype.broadId = broadId;
Fiip.prototype.Link = Link;
Fiip.prototype.Format = Format;
Fiip.prototype.listener = listener;
Fiip.prototype.route = route;
Fiip.prototype.checking = checking;
Fiip.centerId = centerId;
Fiip.broadId = broadId;
Fiip.Link = Link;
Fiip.Format = Format;
Fiip.listener = listener;
Fiip.route = route;
Fiip.checking = checking;

const fiip = new Fiip();
module.exports = fiip;
